﻿/*
 * The MIT License (MIT)
 * 
 * Copyright (c) 2012 Brno University of Technology - Faculty of Information Technology (http://www.fit.vutbr.cz)
 * Author(s):
 * Vladimir Vesely (mailto:ivesely@fit.vutbr.cz)
 * Martin Mares
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;

namespace PcapMerger
{
    /// <remarks>
    /// Class implementing support for Microsoft Netmon files
    /// </remarks>
    class PmCaptureMNM : PmCaptureBase
    {

        #region LinkTypeEnumeration
        /// <summary>
        /// Enumeration of Microsoft Network Monitor media types (including non-L2 carriers of information)
        /// </summary>
        public enum MNMMediaType
        {
            Null = 0,
            Ethernet = 1,
            Tokenring = 2,
            FDDI = 3,
            ATM = 4,
            IEEE1394 = 5,
            WiFi = 6,
            TunnelingInterfaces = 7,
            WirelessWAN = 8,
            RawIPFrames = 9,
            UnsupportedPCAPLayer2Type = 0xE000,
            LinuxCookedMode = 0xE071,
            NetEvent = 0xFFE0,
            NetmonNetworkInfoEx = 0xFFFB,
            NetmonPayloadHeader = 0xFFFC,
            NetmonNetworkInfo = 0xFFFD,
            NetmonDNSCache = 0xFFFE,
            NetmonFilter = 0xFFFF
        }
        #endregion

        #region MNMFileVariables

        // MNM variables
        private Byte _mnmVersionMaj;
        private Byte _mnmVersionMin;
        private MNMMediaType _mediaType;
        private DateTime _mnmHeaderTimeStamp;
        private UInt32 _mnmFtOffset;
        private UInt32 _mnmFtLength;
        private UInt32 _mnmPiOffset;
        private UInt32 _mnmPiCount;
        private UInt32 _mnmEiOffset;
        private UInt32 _mnmEiLength;
        private DateTime _mnmEiFileTimeStamp;
        private UInt32 _mnmEiTziCount;
        private UInt32 _mnmNumberOfFrames;
        private List<uint> _mnmPiRecords;
        private List<uint> _mnmEiTziRecords;

        PmSupportedTypes.LinkTypes linkType = PmSupportedTypes.LinkTypes.Null;                   
        Int32 _timeZoneOffset;

        #endregion

        #region Constructors

        public PmCaptureMNM() :
            base(PmSupportedTypes.CaptureFileTypes.MicrosoftNetworkMonitor)
        {
            _timeZoneOffset = 0;
        }

        public PmCaptureMNM(String filePath, BinaryReader fileReader)
            : base(filePath, fileReader,PmSupportedTypes.CaptureFileTypes.MicrosoftNetworkMonitor)
        {
            _timeZoneOffset = 0;
            ParseMNMHeader();
            ParseMNMProcessInfoTable();
            ParseMNMExtendedInfo();
            linkType = ConvertMNMLayer2ToCommonLayer2(_mediaType);
        }

        #endregion

        /// <summary>
        /// First timestamp for LibPcap is timestamp of first frame
        /// </summary>
        /// <returns>Returns DateTime of first frame</returns>
        override public DateTime? FirstTimeStamp()
        {
            //Read timestamp of first frame on offset 24
            return _mnmFtOffset != 0
                       ? ConvertMNMFrameTimeOffsetToRealTime(_mnmHeaderTimeStamp.AddSeconds(_timeZoneOffset),
                                                             GetMNMFlTimeOffsetLocal(_mnmFtOffset)).ToUniversalTime()
                       : (DateTime?) null;

        }

        #region IPMCaptureFileIOMethods

        override public void CreateFrameTable()
        {
            if (BinReader != null)
                ParseMNMFrameTable();
        }

        override public bool CreateOutput(String fileName)
        {
            return CreateMNMOutput(fileName);
        }

        #endregion

        #region TCPDtoSupprotedtypesConversionMethods

        /// <summary>
        /// Converts MNM L2 type to appropriate coommon suppored type
        /// </summary>
        /// <param name="mediaType">Input enumeration MNMMediaType</param>
        /// <returns>Returns converted coomon link type</returns>
        private PmSupportedTypes.LinkTypes ConvertMNMLayer2ToCommonLayer2(MNMMediaType mediaType)
        {
            switch (mediaType)
            {
                case MNMMediaType.Ethernet: return PmSupportedTypes.LinkTypes.Ethernet;
                case MNMMediaType.WiFi: return PmSupportedTypes.LinkTypes.IEEE80211;
                case MNMMediaType.ATM: return PmSupportedTypes.LinkTypes.ATMRfc1483;
                case MNMMediaType.RawIPFrames: return PmSupportedTypes.LinkTypes.Raw;
                case MNMMediaType.FDDI: return PmSupportedTypes.LinkTypes.FDDI;
                default: return PmSupportedTypes.LinkTypes.Null;
            }
        }

        private MNMMediaType ConvertCommonLayer2ToMNMLayer2(PmSupportedTypes.LinkTypes mediaType)
        {
            switch (mediaType)
            {
                case PmSupportedTypes.LinkTypes.Ethernet: return MNMMediaType.Ethernet;
                case PmSupportedTypes.LinkTypes.IEEE80211: return MNMMediaType.WiFi;
                case PmSupportedTypes.LinkTypes.ATMRfc1483: return MNMMediaType.ATM;
                case PmSupportedTypes.LinkTypes.Raw: return MNMMediaType.RawIPFrames;
                case PmSupportedTypes.LinkTypes.FDDI: return MNMMediaType.FDDI;
                default: return MNMMediaType.Null;
            }
        }

        #endregion

        #region MNM Functions

        #region MNM Capture File Header parsing functions

        /// <summary>
        /// Function returns minor version value currently located on 4th byte from start
        /// </summary>
        /// <returns>Returns minor version value from MNM PCAP</returns>
        private Byte GetMNMCfhVersionMin()
        {
            //binreader.BaseStream.Position = 4;
            BinReader.BaseStream.Seek(4, SeekOrigin.Begin);
            return BinReader.ReadByte();
        }

        /// <summary>
        /// Function returns major version value currently located on 5th byte from start
        /// </summary>
        /// <returns>Returns major version value from MNM PCAP</returns>
        private Byte GetMNMCfhVersionMaj()
        {
            //binreader.BaseStream.Position = 5;
            BinReader.BaseStream.Seek(5, SeekOrigin.Begin);
            return BinReader.ReadByte();
        }

        /// <summary>
        /// Function returns MNM media type currently located on 6th byte from start
        /// </summary>
        /// <returns>Returns media type from MNMMediaType enum from Capture File Header of MNM PCAP</returns>
        private MNMMediaType GetMNMCfhMediaType()
        {
            //binreader.BaseStream.Position = 6;
            BinReader.BaseStream.Seek(6, SeekOrigin.Begin);
            return (MNMMediaType)BinReader.ReadUInt16();
        }

        /// <summary>
        /// Function reads 16B long time information starting on 8th byte and converting it to appropriate C# DateTime variable
        /// </summary>
        /// <returns>Returns the time stamp from Capture File Header of MNM PCAP</returns>
        private DateTime GetMNMCfhDateTime()
        {
            //binreader.BaseStream.Position = 8;
            BinReader.BaseStream.Seek(8, SeekOrigin.Begin);
            return ConvertByteArrToDateTime(BinReader.ReadBytes(16));
        }

        /// <summary>
        /// Function gets starting offset of Frame Table currently located on 24th byte from start
        /// </summary>
        /// <returns>Returns starting offset of Frame Table from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhFrameTableOffset()
        {
            //binreader.BaseStream.Position = 24;
            BinReader.BaseStream.Seek(24, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets length of Frame Table currently located on 28th byte from start
        /// </summary>
        /// <returns>Returns length of Frame Table from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhFrameTableLength()
        {
            //binreader.BaseStream.Position = 28;
            BinReader.BaseStream.Seek(28, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets starting offset of User Data currently located on 32nd byte from start
        /// </summary>
        /// <returns>Returns starting offset of User Data from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhUserDataOffset()
        {
            //binreader.BaseStream.Position = 32;
            BinReader.BaseStream.Seek(32, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets length of User Data currently located on 36th byte from start
        /// </summary>
        /// <returns>Returns length of User Data from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhUserDataLength()
        {
            //binreader.BaseStream.Position = 36;
            BinReader.BaseStream.Seek(36, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets starting offset of Comment Info currently located on 40th byte from start
        /// </summary>
        /// <returns>Returns starting offset of Comment Info from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhCommentInfoOffset()
        {
            //binreader.BaseStream.Position = 40;
            BinReader.BaseStream.Seek(40, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets length of Comment Info currently located on 44th byte from start
        /// </summary>
        /// <returns>Returns length of Comment Info from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhCommentInfoLength()
        {
            //binreader.BaseStream.Position = 44;
            BinReader.BaseStream.Seek(44, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        ///  Function gets starting offset of Process Info Table currently located on 48th byte from start
        /// </summary>
        /// <returns>Returns starting offset of Process Info Table from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhProcessInfoTableOffset()
        {
            //binreader.BaseStream.Position = 48;
            BinReader.BaseStream.Seek(48, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets number of records in Process Info Table currently located on 52nd byte from start
        /// </summary>
        /// <returns>Returns number of records in Process Info Table</returns>
        private UInt32 GetMNMCfhProcessInfoTableCount()
        {
            //binreader.BaseStream.Position = 52;
            BinReader.BaseStream.Seek(52, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets starting offset of Extended Info currently located on 56th byte from start
        /// </summary>
        /// <returns>Returns starting offset of Extended Info from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhExtendedInfoOffset()
        {
            //binreader.BaseStream.Position = 56;
            BinReader.BaseStream.Seek(56, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets number of records in Process Info Table currently located on 60th byte from start
        /// </summary>
        /// <returns>Returns length of Process Info Table from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhExtendedInfoLength()
        {
            //binreader.BaseStream.Position = 60;
            BinReader.BaseStream.Seek(60, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets starting offset of Conversation Stats currently located on 64th byte from start
        /// </summary>
        /// <returns>Returns starting offset of Conversation Stats from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhConversationStatsOffset()
        {
            //binreader.BaseStream.Position = 64;
            BinReader.BaseStream.Seek(64, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Function gets length of Conversation Stats currently located on 68th byte from start
        /// </summary>
        /// <returns>Returns length of Conversation Stats from Capture File Header in MNM PCAP file</returns>
        private UInt32 GetMNMCfhConversationStatsLength()
        {
            //binreader.BaseStream.Position = 68;
            BinReader.BaseStream.Seek(68, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        #endregion

        #region MNM Process Info Table parsing functions

        /// <summary>
        /// Function gets record from MNM Process Info Table on current offset and fill return variables with appropriate values
        /// </summary>
        /// <param name="offset">Offset to starting position of record in Process Info Table</param>
        /// <param name="pathSize">Return value for path size of application path</param>
        /// <param name="unicodePath">Return value for application path</param>
        /// <param name="iconSize">Return value for icon size of application</param>
        /// <param name="iconData">Return value for icon data of application</param>
        /// <param name="pid">Return value for windows Process IDentified</param>
        /// <param name="localPort">Return value for local port of socket used by application</param>
        /// <param name="remotePort">Return value for remote port of socket used by application</param>
        /// <param name="isipv6">Return value for bool pragma whether used addresses are IPv6 or not</param>
        /// <param name="localIP">Return value for local IP address of socket used by application</param>
        /// <param name="remoteIP">Return value for local IP address of socket used by application</param>
        /// <returns>Return variables are pathSize, unicodePath, iconSize, iconData, pid, localPort, remotePort, isipv6, localIP and remoteIP</returns>
        private void GetMNMProcessInfoTableRecord(UInt32 offset,
                                                         out UInt32 pathSize, out String unicodePath,
                                                         out UInt32 iconSize, out byte[] iconData, out UInt32 pid,
                                                         out UInt16 localPort, out UInt16 remotePort, out UInt32 isipv6,
                                                         out IPAddress localIP, out IPAddress remoteIP)
        {
            pathSize = GetMNMPitPathSize(offset);
            unicodePath = GetMNMPitUnicodePathToApp(offset);
            iconSize = GetMNMPitIconSize(offset);
            iconData = GetMNMPitIconData(offset);
            pid = GetMNMPitPID(offset);
            localPort = GetMNMPitLocalPort(offset);
            remotePort = GetMNMPitRemotePort(offset);
            isipv6 = GetMNMPitIsipv6(offset);
            localIP = GetMNMPitLocalAddress(offset);
            remoteIP = GetMNMPitRemoteAddress(offset);
        }

        /// <summary>
        /// Function is able to count length of MNM Process Info Length with given starting index
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns length of MNM Process Info Table record starting on target offset</returns>
        private UInt32 CountMNMPiRecordLength(UInt32 offset)
        {
            //return offset + 4 + GetMNMPitPathSize(binreader, offset) + 4 + GetMNMPitIconSize(binreader, offset) + 4 + 2 + 2 + 2 + 2 + 4 + 16 + 16;
            return offset + 4 + GetMNMPitPathSize(offset) + 4 + GetMNMPitIconSize(offset) + 48;
        }

        /// <summary>
        /// Reads MNM Process Info Table version for parsing purpouses, current version is 2
        /// </summary>
        /// <returns>Returns version of MNM Process Info Table</returns>
        private UInt16 GetMNMPitVersion()
        {
            //sfs.BinReader.BaseStream.Position = sfs.MNMPiOffset;
            BinReader.BaseStream.Seek(_mnmPiOffset, SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads 4B long application path size in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns application path size of Process Info Table record</returns>
        private UInt32 GetMNMPitPathSize(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset;
            BinReader.BaseStream.Seek(offset, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads n-bytes long application path in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns application path of Process Info Table record. Returns Empty string if path is not available.</returns>
        private String GetMNMPitUnicodePathToApp(UInt32 offset)
        {
            var delka = GetMNMPitPathSize(offset);
            if (delka == 0)
                return String.Empty;
            //binreader.BaseStream.Position = offset + 4;
            BinReader.BaseStream.Seek(offset + 4, SeekOrigin.Begin);
            //var cesta = binreader.ReadChars((int)delka);
            return BinReader.ReadChars((int)delka).ToString();
        }

        /// <summary>
        /// Reads 4B long application icon size in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns application icon size of Process Info Table record</returns>
        private UInt32 GetMNMPitIconSize(UInt32 offset)
        {
            var delka = GetMNMPitPathSize(offset);
            //binreader.BaseStream.Position = offset + 4 + delka;
            BinReader.BaseStream.Seek(offset + 4 + delka, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads n-byte long application icon data in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns application icon as byte array. Returns null if icon data are not available.</returns>
        private byte[] GetMNMPitIconData(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            if (delka2 == 0)
                return null;
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4;
            BinReader.BaseStream.Seek(offset + delka1 + 8, SeekOrigin.Begin);
            return BinReader.ReadBytes((int)delka2);
        }

        /// <summary>
        /// Reads 4B long application PID in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns unique Windows application Process IDentifier of Process Info Table record</returns>
        private UInt32 GetMNMPitPID(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2;
            BinReader.BaseStream.Seek(offset + delka1 + 8 + delka2, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads 2B long local port used by application in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns local (source) port used by application in Process Info Table record</returns>
        private UInt16 GetMNMPitLocalPort(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2 + 4;
            BinReader.BaseStream.Seek(offset + delka1 + 12 + delka2, SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads 2B long remote port used by application in given Process Info Table record starting on target offset
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns remote (destination) port used by application in Process Info Table record</returns>
        private UInt16 GetMNMPitRemotePort(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2 + 4 + 2 + 2;
            BinReader.BaseStream.Seek(offset + delka1 + 16 + delka2, SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads 4B long pragma value whether IP addresses used in given Process Info Table are IPv6 or not
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Return value different than 0 means IPv6 address.</returns>
        private UInt32 GetMNMPitIsipv6(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2 + 4 + 2 + 2 + 2 + 2;
            BinReader.BaseStream.Seek(offset + delka1 + 20 + delka2, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads either 4B long IPv4 or 16B long IPv6 local (source) address of application in given Process Info Table record
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns local (source) IP address used by application in Process Info Table record</returns>
        private IPAddress GetMNMPitLocalAddress(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            var isipv6 = GetMNMPitIsipv6(offset) == 0;
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2 + 4 + 2 + 2 + 2 + 2 + 4;
            BinReader.BaseStream.Seek(offset + delka1 + 24 + delka2, SeekOrigin.Begin);
            return isipv6 ? new IPAddress(BinReader.ReadBytes(4)) : new IPAddress(BinReader.ReadBytes(16));
        }

        /// <summary>
        /// Reads either 4B long IPv4 or 16B long IPv6 remote (destination) address of application in given Process Info Table record
        /// </summary>
        /// <param name="offset">Offset pointing to start of MNM Process Info Table record</param>
        /// <returns>Returns remote (destination) port used by application in Process Info Table record</returns>
        private IPAddress GetMNMPitRemoteAddress(UInt32 offset)
        {
            var delka1 = GetMNMPitPathSize(offset);
            var delka2 = GetMNMPitIconSize(offset);
            var isipv6 = GetMNMPitIsipv6(offset) == 0;
            //binreader.BaseStream.Position = offset + 4 + delka1 + 4 + delka2 + 4 + 2 + 2 + 2 + 2 + 4 + 16;
            BinReader.BaseStream.Seek(offset + delka1 + 40 + delka2, SeekOrigin.Begin);
            return isipv6 ? new IPAddress(BinReader.ReadBytes(4)) : new IPAddress(BinReader.ReadBytes(16));
        }

        #endregion

        #region MNM Extended Info parsing functions

        /// <summary>
        /// Counts length of TZI record as result of = offset + 172B 
        /// </summary>
        /// <param name="offset">Startting offset</param>
        /// <returns>In fact it returns starting offset of next TZI record</returns>
        private static UInt32 CountEiTZIRecordLength(UInt32 offset)
        {
            //return offset + 4 + 64 + 16 + 4 + 64 + 16 + 4;
            return offset + 172;
        }

        /// <summary>
        /// Reads 2B long version value of MNM Extended Info, use for parsing purpouses
        /// </summary>
        /// <returns>Returns value of version in MNM Extended Info</returns>
        private UInt16 GetMNMEiVersion()
        {
            //binreader.BaseStream.Position = GetMNMCfhExtendedInfoOffset(binreader);
            BinReader.BaseStream.Seek(GetMNMCfhExtendedInfoOffset(), SeekOrigin.Begin);
            return BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads 2B long FileTime value in MNM Extended Info and converts it to DateTime
        /// </summary>
        /// <returns>Returns DateTime value of FileTime converted to UTC time</returns>
        private DateTime GetMNMEiFileTime()
        {
            //binreader.BaseStream.Position = GetMNMCfhExtendedInfoOffset(binreader) + 2;
            BinReader.BaseStream.Seek(GetMNMCfhExtendedInfoOffset() + 2, SeekOrigin.Begin);
            return DateTime.FromFileTimeUtc(BinReader.ReadInt64());
        }

        /// <summary>
        /// Reads 1B long count of TZI records in MNM Extended Info
        /// </summary>
        /// <returns>Return number of TZI records</returns>
        private Byte GetMNMEiCountTzi()
        {
            //binreader.BaseStream.Position = GetMNMCfhExtendedInfoOffset(binreader) + 2 + 8;
            BinReader.BaseStream.Seek(GetMNMCfhExtendedInfoOffset() + 10, SeekOrigin.Begin);
            return BinReader.ReadByte();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ofset"></param>
        /// <param name="bias"></param>
        /// <param name="standardName"></param>
        /// <param name="standardDate"></param>
        /// <param name="standardBias"></param>
        /// <param name="daylightName"></param>
        /// <param name="daylightDate"></param>
        /// <param name="daylightBias"></param>
        private void GetMNMEiTziRecord(UInt32 ofset, out Int32 bias,
                                              out String standardName, out DateTime standardDate, out Int32 standardBias,
                                              out String daylightName, out DateTime daylightDate, out Int32 daylightBias)
        {
            //binreader.BaseStream.Position = ofset;
            BinReader.BaseStream.Seek(ofset, SeekOrigin.Begin);
            bias = BinReader.ReadInt32();
            standardName = new string(BinReader.ReadChars(64));
            standardDate = ConvertByteArrToDateTime(BinReader.ReadBytes(16));
            standardBias = BinReader.ReadInt32();
            daylightName = new string(BinReader.ReadChars(64));
            daylightDate = ConvertByteArrToDateTime(BinReader.ReadBytes(16));
            daylightBias = BinReader.ReadInt32();
        }

        /// <summary>
        /// Reads 4B long bias (difference in minutes between UTC in local TZ) value in MNM Extended Info record starting on given offset
        /// </summary>
        /// <param name="binreader">Binary reader of opened PCAP file</param>
        /// <param name="offset">Starting offset of TZI record</param>
        /// <returns>Returns bias value from target TZI record</returns>
        private static Int32 GetMNMTziRecordBias(BinaryReader binreader, UInt32 offset)
        {
            //binreader.BaseStream.Position = offset;
            binreader.BaseStream.Seek(offset, SeekOrigin.Begin);
            return binreader.ReadInt32();
        }

        #endregion

        #region MNM Frame Table parsing functions

        /// <summary>
        /// Gets 8B long time offset from Frame Layout which represents delta offset from base t time in Capture File Header. Actual timestamp is acquired as = t + delta.
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns time offset value since beginning of capture</returns>
        internal  UInt64 GetMNMFlTimeOffsetLocal(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset;
            BinReader.BaseStream.Seek(offset, SeekOrigin.Begin);
            return BinReader.ReadUInt64();
        }

        /// <summary>
        /// Reads 4B long value starting on 8th byte of Frame Layout which represents actual length/size of frame as it was received by network adapter
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns actual length of frame in bytes as captured by network interface card</returns>
        private UInt32 GetMNMFlFrameLengthWire(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset + 8;
            BinReader.BaseStream.Seek(offset + 8, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads 4B long value starting on 12th byte of Frame Layout which represents length/size of frame data stored in this PCAP file 
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns stored length of frame in bytes in this PCAP file. It could by smaller than previous wire-length.</returns>
        private UInt32 GetMNMFlFrameLength(UInt32 offset)
        {
            //binreader.BaseStream.Position = offset + 8 + 4;
            BinReader.BaseStream.Seek(offset + 12, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

      /*  /// <summary>
        /// Reads n-bytes starting on 16th byte of Frame Layout  of frame data present in PCAP file
        /// </summary>
        /// <param name="binreader">Binary reader with opened PCAP file</param>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <param name="length"></param>
        /// <returns>Returns byte array of raw frame data</returns>
        private byte[] GetMNMFlFrameData(UInt32 offset, UInt32 length)
        {
            //binreader.BaseStream.Position = offset + 8 + 4 + 4;
            BinReader.BaseStream.Seek(offset + 16, SeekOrigin.Begin);
            return BinReader.ReadBytes((int)length);
        }
        */

        /// <summary>
        /// Reads 2B long value starting on zero byte after frame data of Frame Layout which represents of Layer 2 type of media on which frame was captured
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns MNMMediaType enumeration as representation of return value. If version of MNM Pcap file is 2.0 which misses per frame information it returns Media Type in Capture Header.</returns>
        private MNMMediaType GetMNMFlMediaType(UInt32 offset)
        {
            /* MNM version 2.0 issue */
            if (_mnmVersionMaj == 2 && _mnmVersionMin == 0)
                return _mediaType;
            var len = GetMNMFlFrameLength(offset);
            //sfs.BinReader.BaseStream.Position = offset + 8 + 4 + 4 + len;
            BinReader.BaseStream.Seek(offset + 16 + len, SeekOrigin.Begin);
            return (MNMMediaType)BinReader.ReadUInt16();
        }

        /// <summary>
        /// Reads 4B long value starting 2nd byte after frame data of Frame Layout which which represents index to MNM Process Info Table where more detail information about which application sent/received this network frame
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns index to MNM Process Info Table or returns 0, if no Process Info Table is present.</returns>
        private UInt32 GetMNMFlProcessInfoIndex(UInt32 offset)
        {
            if (_mnmPiOffset == 0) return 0;
            var len = GetMNMFlFrameLength(offset);
            //sfs.BinReader.BaseStream.Position = offset + 8 + 4 + 4 + len + 2;
            BinReader.BaseStream.Seek(offset + 18 + len, SeekOrigin.Begin);
            return BinReader.ReadUInt32();
        }

        /// <summary>
        /// Reads 8B long value starting 6th byte after frame data of Frame Layout which which represents FILETIME when frame was received by OS
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns UTC representation of FILETIME converted according to local timezone settings. Returns 0001-01-01 in case of error.</returns>
        private DateTime GetMNMFlFileTimeStamp(UInt32 offset)
        {
            /* If no Extended info is present in PCAP then return minimal DateTime 0001-01-01 */
            if (_mnmEiOffset == 0)
                return DateTime.MinValue;
            var len = GetMNMFlFrameLength(offset);
            //sfs.BinReader.BaseStream.Position = offset + 8 + 4 + 4 + len + 2 + 4;
            BinReader.BaseStream.Seek(offset + 22 + len, SeekOrigin.Begin);
            //PrintError("--- " + aaa + " --- " + DateTime.FromFileTimeUtc(aaa).ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            return DateTime.FromFileTimeUtc(BinReader.ReadInt64());
        }

        /// <summary>
        /// Reads 4B long value starting 14th byte after frame data of Frame Layout which which represents index to MNM Process Info Table where more detail information about which application sent/received this network frame
        /// </summary>
        /// <param name="offset">Starting offset of frame layout in MNM frame table</param>
        /// <returns>Returns index to TZI table in MNM Extended Info. Returns 255 in case of error.</returns>
        private Byte GetMNMFlTzi(UInt32 offset)
        {
            if (_mnmEiOffset == 0)
                return Byte.MaxValue;
            var len = GetMNMFlFrameLength(offset);
            //sfs.BinReader.BaseStream.Position = offset + 8 + 4 + 4 + len + 2 + 4 + 8;
            BinReader.BaseStream.Seek(offset + 30 + len, SeekOrigin.Begin);
            return BinReader.ReadByte();
        }

        /// <summary>
        /// Function returns variables filled with values belonging to one frame in MNM Frame Layout
        /// </summary>
        /// <param name="frame">Target frame</param>
        /// <param name="timeOffsetLocal">Time offset from base time</param>
        /// <param name="frameLengthWire">Real size of frame just as it was processed by capturing engine</param>
        /// <param name="frameLength">Actual size of frame stored in PCAP file</param>
        /// <param name="frameData">Binary frame data</param>
        /// <param name="frameMediaType">Layer 2 type of media on which intercept was done</param>
        /// <param name="framePiIndex">Index to MNM Process Info Table of PCAP file</param>
        /// <param name="frameFileTime">DateTime variable with converted FILETIME time stamp</param>
        /// <param name="frameTZIIndex">Index to TZI Table in Extended Info of PCAP file</param>
        private void GetMNMFlFrameRecord(PmFrameMNM frame, out UInt64 timeOffsetLocal,
                                                out UInt32 frameLengthWire, out UInt32 frameLength, out byte[] frameData,
                                                out MNMMediaType frameMediaType, out UInt32 framePiIndex,
                                                out DateTime frameFileTime, out UInt32 frameTZIIndex)
        {
            timeOffsetLocal = GetMNMFlTimeOffsetLocal(frame.FrameOffset());
            frameLengthWire = GetMNMFlFrameLengthWire(frame.FrameOffset());
            frameLength = GetMNMFlFrameLength(frame.FrameOffset());
           // frameData = GetMNMFlFrameData(offset, frameLength);
            frameData = frame.Data();
            frameMediaType = GetMNMFlMediaType(frame.FrameOffset());
            framePiIndex = GetMNMFlProcessInfoIndex(frame.FrameOffset());
            frameFileTime = GetMNMFlFileTimeStamp(frame.FrameOffset());
            frameTZIIndex = GetMNMFlTzi(frame.FrameOffset());
        }

        #endregion

        #region MNM printing functions

        /// <summary>
        /// Print all information stored in MNM Capture File Header to Console output
        /// </summary>
        public void PrintMNMCaptureHeader()
        {
            PmConsolePrinter.PrintDebug("\n\nCAPTURE FILE HEADER INFORMATION FOR " + Path.GetFileName(FilePath));
            PmConsolePrinter.PrintInfoEol("Version> {0}.{1}\tMediaType> {2}", _mnmVersionMaj, _mnmVersionMin, _mediaType);
            PmConsolePrinter.PrintInfoEol("Timestamp> {0}", _mnmHeaderTimeStamp.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            PmConsolePrinter.PrintInfoEol("Frame Table Offset> {0}", _mnmFtOffset);
            PmConsolePrinter.PrintInfoEol("Frame Table Length> {0}", _mnmFtLength);
            PmConsolePrinter.PrintInfoEol("User Data Offset> {0}", GetMNMCfhUserDataOffset());
            PmConsolePrinter.PrintInfoEol("User Data Length> {0}", GetMNMCfhUserDataLength());
            PmConsolePrinter.PrintInfoEol("Comment Info Offset> {0}", GetMNMCfhCommentInfoOffset());
            PmConsolePrinter.PrintInfoEol("Comment Info Length> {0}", GetMNMCfhCommentInfoLength());
            PmConsolePrinter.PrintInfoEol("Process Info Table Offset> {0}", _mnmPiOffset);
            PmConsolePrinter.PrintInfoEol("Process Info Table Count> {0}", _mnmPiCount);
            PmConsolePrinter.PrintInfoEol("Extended Info Offset> {0}", _mnmEiOffset);
            PmConsolePrinter.PrintInfoEol("Extended Info Length> {0}", _mnmEiLength);
            PmConsolePrinter.PrintInfoEol("Conversation Stats Offset> {0}", GetMNMCfhConversationStatsOffset());
            PmConsolePrinter.PrintInfoEol("Conversation Stats Length> {0}", GetMNMCfhConversationStatsLength());
            PmConsolePrinter.PrintInfoEol("Number of frames> {0}", _mnmNumberOfFrames);
        }

        /// <summary>
        /// Print all information stored in MNM Process Infor Table to Console output
        /// </summary>
        public void PrintMNMProcessInfoTable()
        {
            PmConsolePrinter.PrintDebug("\n\nPROCESS INFO TABLE INFORMATION FOR " + Path.GetFileName(FilePath));
            PmConsolePrinter.PrintInfoEol("Starting offset> {0}", _mnmPiOffset);
            PmConsolePrinter.PrintInfoEol("Number of records> {0}", _mnmPiRecords.Count);
            PmConsolePrinter.PrintInfoEol("Version> {0}", GetMNMPitVersion());
            /* Print */
            foreach (var ofs in _mnmPiRecords)
            {
                Console.WriteLine("\nRecord: {0}, offset {1}:", _mnmPiRecords.IndexOf(ofs), ofs);
                PrintMNMPiRecord(ofs);
            }
        }

        /// <summary>
        /// Print all information in one Process Info record starting on given offset
        /// </summary>
        /// <param name="offset">Starting offset of given Process Info record</param>
        public void PrintMNMPiRecord(uint offset)
        {
            UInt32 pathSize;
            String unicodePath;
            UInt32 iconSize;
            byte[] iconData;
            UInt32 pid;
            UInt16 localPort;
            UInt16 remotePort;
            UInt32 isipv6;
            IPAddress localIP;
            IPAddress remoteIP;
            GetMNMProcessInfoTableRecord(offset, out pathSize, out unicodePath, out iconSize,
                                         out iconData,
                                         out pid, out localPort, out remotePort, out isipv6, out localIP,
                                         out remoteIP);
            PmConsolePrinter.PrintInfoEol("\nPath size> {0}", pathSize);
            PmConsolePrinter.PrintInfoEol("Unicode application path> {0}", unicodePath);
            PmConsolePrinter.PrintInfoEol("Icon Size> {0}", iconSize);
            PmConsolePrinter.PrintInfoEol("Icon Data> {0}", iconData);
            PmConsolePrinter.PrintInfoEol("PID> {0}", pid);
            PmConsolePrinter.PrintInfoEol("Local port> {0}", localPort);
            PmConsolePrinter.PrintInfoEol("Remote port> {0}", remotePort);
            PmConsolePrinter.PrintInfoEol("Is IPv6 address> {0}", isipv6 != 0 ? "YES" : "NO");
            PmConsolePrinter.PrintInfoEol("Local IP address> {0}", localIP);
            PmConsolePrinter.PrintInfoEol("Remote IP address> {0}", remoteIP);
        }

        /// <summary>
        /// Print all information stored in MNM Extended Information to Console output
        /// </summary>
        public void PrintMNMExtendedInfo()
        {
            PmConsolePrinter.PrintDebug("\n\nEXTENDED INFO INFORMATION FOR " + Path.GetFileName(FilePath));
            PmConsolePrinter.PrintInfoEol("Version> {0}", GetMNMEiVersion());
            PmConsolePrinter.PrintInfoEol("Start time UTC> {0}", _mnmEiFileTimeStamp.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            PmConsolePrinter.PrintInfoEol("Number of country TZIs> {0}", _mnmEiTziCount);
            foreach (var ofs in _mnmEiTziRecords)
            {
                Console.WriteLine("\nTZI {0}, offset {1}:", _mnmEiTziRecords.IndexOf(ofs), ofs);
                PrintMNMEiTziRecord(ofs);
            }
        }

        /// <summary>
        /// Print all information of given TZI record
        /// </summary>
        /// <param name="offset">Starting offset of given TZI record</param>
        public void PrintMNMEiTziRecord(uint offset)
        {
            Int32 bias;
            String standardName;
            DateTime standardDate;
            Int32 standardBias;
            String daylightName;
            DateTime daylightDate;
            Int32 daylightBias;
            GetMNMEiTziRecord(offset, out bias, out standardName, out standardDate, out standardBias,
                              out daylightName, out daylightDate, out daylightBias);
            PmConsolePrinter.PrintInfoEol("Bias> {0}", bias);
            PmConsolePrinter.PrintInfoEol("Standard Name> {0}", standardName);
            PmConsolePrinter.PrintInfoEol("Standard Date> {0}", standardDate.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            PmConsolePrinter.PrintInfoEol("Standard Bias> {0}", standardBias);
            PmConsolePrinter.PrintInfoEol("Daylight Name> {0}", daylightName);
            PmConsolePrinter.PrintInfoEol("Daylight Date> {0}", daylightDate.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            PmConsolePrinter.PrintInfoEol("Daylight Bias> {0}", daylightBias);
        }

        /// <summary>
        /// Print all information of given frame to Console output
        /// </summary>
        /// <param name="frame">Target frame</param>
        public void PrintMNMFrameLayoutRecord(PmFrameMNM frame)
        {
            UInt64 timeOffsetLocal;
            UInt32 frameLengthWire;
            UInt32 frameLength;
            byte[] frameData;
            MNMMediaType frameMediaType;
            UInt32 framePiIndex;
            DateTime frameFileTime;
            UInt32 frameTZIIndex;

            GetMNMFlFrameRecord(frame, out timeOffsetLocal, out frameLengthWire, out frameLength, out frameData,
                                out frameMediaType, out framePiIndex, out frameFileTime, out frameTZIIndex);

            PmConsolePrinter.PrintInfoEol("Time offset local> {0}", timeOffsetLocal);
            PmConsolePrinter.PrintInfoEol("Real time> {0}",
                              ConvertMNMFrameTimeOffsetToRealTime(_mnmHeaderTimeStamp, timeOffsetLocal).ToString(
                                  "dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            PmConsolePrinter.PrintInfoEol("Frame length wire> {0}", frameLengthWire);
            PmConsolePrinter.PrintInfoEol("Frame length> {0}", frameLength);
            PmConsolePrinter.PrintInfoEol("Frame data> {0}", frameData);

            PmConsolePrinter.PrintInfo("Media type> ");
            if (_mnmVersionMaj == 2 && _mnmVersionMin == 0) PmConsolePrinter.PrintError(frameMediaType.ToString());
            else PmConsolePrinter.PrintInfoEol("{0}",frameMediaType);


            PmConsolePrinter.PrintInfo("Process Info index> ");
            if (framePiIndex == 0xFFFFFFFF || framePiIndex == 0) PmConsolePrinter.PrintError("N/A");
            else PmConsolePrinter.PrintInfoEol("{0}",framePiIndex);

            PmConsolePrinter.PrintInfo("Time stamp> ");
            if (frameFileTime.CompareTo(new DateTime(1, 1, 1)) <= 0) PmConsolePrinter.PrintError("N/A");
            else PmConsolePrinter.PrintInfoEol(frameFileTime.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));

            PmConsolePrinter.PrintInfo("TZI index> ");
            if (frameTZIIndex == Byte.MaxValue) PmConsolePrinter.PrintError("N/A");
            else PmConsolePrinter.PrintInfoEol("{0}",frameTZIIndex);

        }

        /// <summary>
        /// Prints all information stored in MNM Frame Layout to Console output
        /// </summary>
        public void PrintMNMFrameTable()
        {
            PmConsolePrinter.PrintDebug("\n\nFRAME TABLE INFORMATION FOR " + Path.GetFileName(FilePath));
            PmConsolePrinter.PrintInfoEol("Frame Table Offset> {0}",_mnmFtOffset);
            PmConsolePrinter.PrintInfoEol("Frame Table Length> {0}", _mnmFtLength);
            PmConsolePrinter.PrintInfoEol("Number of frames> {0}", _mnmNumberOfFrames);
            foreach (var fr in Frames.Where(fr => fr.Type() == PmSupportedTypes.CaputreFileFrameType.MNM))
            {
                PmConsolePrinter.PrintInfoEol("\nFrame {0}, offset {1}:", Frames.IndexOf(fr), fr.FrameOffset());
                PrintMNMFrameLayoutRecord((PmFrameMNM)fr);
            }
        }

        #endregion

        #region MNM misceleanous functions

        public bool HasProcessInfoTable()
        {
            return _mnmPiOffset != 0;
        }

        public bool HasExtendedInfo()
        {
            return _mnmEiOffset != 0;
        }

        /// <summary>
        /// Convert 16B long DATETIME data type to C# DateTime type
        /// </summary>
        /// <param name="field">16B long byte array</param>
        /// <returns>DateTime instance created from DATETIME</returns>
        private static DateTime ConvertByteArrToDateTime(byte[] field)
        {
            //for (int i = 0; i< pole.Length; i++) Console.WriteLine(pole[i]);
            var fsm = new BinaryReader(new MemoryStream(field));
            var year = fsm.ReadUInt16();
            //TODO: Doesn't commenting bellow cause any harm?
            //if (year == 0) year = 2000;
            var month = fsm.ReadUInt16();
            //var dayofweek = fsm.ReadUInt16();
            fsm.ReadUInt16();
            var day = fsm.ReadUInt16();
            var hour = fsm.ReadUInt16();
            var minute = fsm.ReadUInt16();
            var second = fsm.ReadUInt16();
            var milisec = fsm.ReadUInt16();
            //var str = year + "-" + month + "-" + day + " (" + dayofweek + ") " + hour + ":" + minute + ":" + minute + ":" + second + "." + milisec;                        
            return new DateTime(year, month, day, hour, minute, second, milisec);
        }

        /// <summary>
        /// Convert DateTime variable to 16B long byte-array
        /// </summary>
        /// <param name="dattim">Input DateTime</param>
        /// <returns>16B long byte array consisting of year-month-dayofweek-day-hour-minute-second-milisec</returns>
        private static byte[] ConvertDateTimeToByteArr(DateTime dattim)
        {
            //PrintError(dattim.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF"));
            var year = BitConverter.GetBytes(dattim.Year);
            var month = BitConverter.GetBytes(dattim.Month);
            var dayofweek = BitConverter.GetBytes(Convert.ToUInt16(dattim.DayOfWeek));
            var day = BitConverter.GetBytes(dattim.Day);
            var hour = BitConverter.GetBytes(dattim.Hour);
            var minute = BitConverter.GetBytes(dattim.Minute);
            var second = BitConverter.GetBytes(dattim.Second);
            var milisec = BitConverter.GetBytes(dattim.Millisecond);
            //PrintDebug(hour[0]+"-"+hour[1]);            
            var ms = new MemoryStream();
            ms.Write(year, 0, 2);
            ms.Write(month, 0, 2);
            ms.Write(dayofweek, 0, 2);
            ms.Write(day, 0, 2);
            ms.Write(hour, 0, 2);
            ms.Write(minute, 0, 2);
            ms.Write(second, 0, 2);
            ms.Write(milisec, 0, 2);
            return ms.ToArray();
        }

        /// <summary>
        /// Computes DateTime variable from time base by adding appropriate time offset
        /// </summary>
        /// <param name="timeBase">Base timestamp</param>
        /// <param name="timeOffset">Added time offset</param>
        /// <returns>Returns result of adding time offset to base time</returns>
        internal static DateTime ConvertMNMFrameTimeOffsetToRealTime(DateTime timeBase, UInt64 timeOffset)
        {
            //PrintError("--- " + sfs.MNMHeaderTimeStamp.ToString("dd/MM/yyyy HH:mm:ss.FFFFFFF") + " --- " + timeOffset);
            //var prvni = sfs.MNMHeaderTimeStamp;            
            return timeBase.AddTicks((long)timeOffset * 10);
        }

        /// <summary>
        /// Create output file in format of Microsoft Network Monitor
        /// </summary>
        /// <param name="outFile">Filename</param>
        /// <returns></returns>
        public bool CreateMNMOutput(String outFile)
        {
            try
            {
                var bw = new BinaryWriter(File.Open(Path.GetFullPath(outFile), FileMode.Create));
                /* TCP Dump format global header variables */
                const UInt32 signatura = 0x55424d47;
                const Byte vermin = 3;
                const Byte vermaj = 2;
                //Beginning signature of file "GMBU" and MNM versions
                bw.Write(signatura);
                bw.Write(vermin);
                bw.Write(vermaj);
                //Used mediatype derived from the first frame
               // bw.Write((UInt16)MediaType);

                MNMMediaType newMediaType = MNMMediaType.Null;
                DateTime firstDateTime = new DateTime();
                PmSupportedTypes.LinkTypes referenceLinkType = PmSupportedTypes.LinkTypes.Null;
                if (Frames.Any())
                {
                    referenceLinkType = Frames.First().LinkType();
                    newMediaType = ConvertCommonLayer2ToMNMLayer2(referenceLinkType);
                    firstDateTime = Frames.First().TimeStamp();
                }

                bw.Write((UInt16)newMediaType);
                bw.Write(ConvertDateTimeToByteArr(firstDateTime));
                //Frame table starts on following offset
                const byte mnmFrameHeaderLen = 22;
                var ofs = Frames.Aggregate<IPmFrame, uint>(0x48, (current, fr) => current + (fr.IncludedLength() + mnmFrameHeaderLen));
                bw.Write(ofs);
                //Number of frames in file
                bw.Write(Frames.Count * 4);
                //No userdata table, comment info table, process info table, extended info table, conversation table
                bw.Write(new byte[40]);
                //Write Frame Layout
              //  DateTime prvni = Frames.First().TimeStamp;

                foreach (var fr in Frames.Where(fr => fr.LinkType() == referenceLinkType))
                {
                    //Write time offset in microseconds since beginning of sniffing                    
                    var timeofs = (UInt64)(fr.TimeStamp().Subtract(firstDateTime).Ticks / 10);
                    //PrintDebug(fr.TimeStamp.Ticks + " - " + prvni.Ticks + " = " + timeofs);
                    bw.Write(timeofs);
                    //Write frame sizes
                    bw.Write(fr.OriginalLength());
                    bw.Write(fr.IncludedLength());
                    //Write raw data
                 //   bw.Write(GetMNMFlFrameData(fr.FrameOffset, fr.IncludedLength));
                    bw.Write(fr.Data());
                    //Write mnmmedia type, that should be all same by now
                    bw.Write((UInt16)newMediaType);
                    //No process index present
                    bw.Write(0xffffffff);
                }
                //Write Frame Table                
                UInt32 offset = 0x48;
                foreach (var fr in Frames.Where(fr => fr.LinkType() == referenceLinkType))
                {
                    //PrintDebug(bw.BaseStream.Position + " --- " + offset);
                    bw.Write(offset);
                    offset += fr.IncludedLength() + mnmFrameHeaderLen;
                }

                bw.Close();

            }
            /* If anything bad happened print error and return false */
            catch (Exception ex)
            {
                PmConsolePrinter.PrintError(ex.Message);
                return false;
            }
            /* Otherwise return true */
            return true;
        }

        #endregion

        #region MNM parsing functions

        /// <summary>
        /// Initialization function for MNM Frame Table Layout variables
        /// </summary>
        /// <param name="offset">Starting offset of Frame Table Layout in MNM PCAP file</param>
        /// <param name="length">Length of Frame Table Layout in MNM PCAP file</param>
        public void SetFrameTable(UInt32 offset, UInt32 length)
        {
            _mnmFtOffset = offset;
            _mnmFtLength = length;
            _mnmNumberOfFrames = _mnmFtLength = length / sizeof(UInt32);
        }

        /// <summary>
        /// Initialization function for MNM Extended Info variables
        /// </summary>
        /// <param name="offset">Starting offset of Extended Information in MNM PCAP file</param>
        /// <param name="length">Length of Extended Information in MNM PCAP file</param>
        public void SetExtendedInfo(UInt32 offset, UInt32 length)
        {
            _mnmEiOffset = offset;
            _mnmEiLength = length;
        }

        /// <summary>
        /// Initialization function for MNM Extended Info variables
        /// </summary>
        /// <param name="offset">Starting offset of Process Information in MNM PCAP file</param>
        /// <param name="count">Length of Process Information in MNM PCAP file</param>
        public void SetProcessInfo(UInt32 offset, UInt32 count)
        {
            _mnmPiOffset = offset;
            _mnmPiCount = count;
        }

        /// <summary>
        /// Function parses MNM Capture File Header and initialize all basic MNM properties of Sniff File Specification
        /// </summary>
        internal void ParseMNMHeader()
        {
            //Parse and store everyhting important
            _mnmVersionMaj = GetMNMCfhVersionMaj();
            _mnmVersionMin = GetMNMCfhVersionMin();
            _mnmHeaderTimeStamp = GetMNMCfhDateTime();
            SetFrameTable(GetMNMCfhFrameTableOffset(), GetMNMCfhFrameTableLength());
            SetProcessInfo(GetMNMCfhProcessInfoTableOffset(),
                               GetMNMCfhProcessInfoTableCount());
            SetExtendedInfo(GetMNMCfhExtendedInfoOffset(), GetMNMCfhExtendedInfoLength());
            /* Convert MNM also to common TCPDump format*/
            _mediaType = GetMNMCfhMediaType();

        }

        /// <summary>
        /// Function parses MNM Process Info Table and initialize all relevant properties in Sniff File Specification
        /// </summary>
        internal void ParseMNMProcessInfoTable()
        {
            /* Verify that Process Info Table is present in PCAP */
            if (_mnmPiOffset == 0)
            {
                PmConsolePrinter.PrintError("No Process Info Table present in " + Path.GetFileName(FilePath));
                return;
            }
            /* Initialize list of ofsets for processes in Process Info Table */
            _mnmPiRecords = new List<uint> { _mnmPiOffset + 2 };
            /* Add offset for each record after the first one */
            for (var i = 0; i < _mnmPiCount - 1; i++)
                _mnmPiRecords.Add(CountMNMPiRecordLength(_mnmPiRecords.Last()));
        }

        /// <summary>
        /// Function parses MNM Extended Info and initialize all relevant properties in Sniff File Specification
        /// </summary>
        internal void ParseMNMExtendedInfo()
        {
            /* Verify existence in PCAP */
            if (_mnmEiOffset == 0)
            {
                PmConsolePrinter.PrintError("No Extended Info present in " + Path.GetFileName(FilePath));
                return;
            }
            /* Initialize variables and list of TZI records */
            _mnmEiFileTimeStamp = GetMNMEiFileTime();
            _mnmEiTziCount = GetMNMEiCountTzi();
            _mnmEiTziRecords = new List<uint>();
            if (_mnmEiTziCount == 0) return;
            _mnmEiTziRecords.Add(_mnmEiOffset + 2 + 8 + 1);
            /* Add offset for each TZI record after the first one */
            for (var i = 0; i < _mnmEiTziCount - 1; i++)
                _mnmEiTziRecords.Add(CountEiTZIRecordLength(_mnmEiTziRecords.Last()));
            /* Use first TZI informatio to set TimeZoneIndex for TCPDump output information 
               multiply it by 60 seconds because it is in minutes 
               TODO: But is this right? */
            
            _timeZoneOffset = 60 * GetMNMTziRecordBias(BinReader, _mnmEiTziRecords.First());
        }

        /// <summary>
        /// Function parses MNM Frame Table and initialize all relevant properties in Sniff File Specification, but most notable it creates MNMFrameTable
        /// </summary>
        internal void ParseMNMFrameTable()
        {
            /* Verify existence in PCAP */
            if (_mnmFtOffset == 0)
            {
                PmConsolePrinter.PrintError("No Frame Table present in " + Path.GetFileName(FilePath));
                return;
            }
            /* Initialize list of Frame Layout records */
            //sfs.MNMFrameTable = new List<uint>();
            /* Add each Frame Layout offset to list */
            for (var i = 0; i < _mnmNumberOfFrames; i++)
            {
                //sfs.BinReader.BaseStream.Position = sfs.MNMFtOffset + i*4;
                BinReader.BaseStream.Seek(_mnmFtOffset + i * 4, SeekOrigin.Begin);
                var flay = BinReader.ReadUInt32();
                //sfs.MNMFrameTable.Add(flay);

                /* MNM version 2.0 issue where all frames in file belongs to one MNMMediaType */
                if (_mnmVersionMaj == 2 && _mnmVersionMin == 0)
                {
                    Frames.Add(new PmFrameMNM(BinReader,
                                                   flay,
                                                   ConvertMNMFrameTimeOffsetToRealTime(_mnmHeaderTimeStamp.AddSeconds(_timeZoneOffset), GetMNMFlTimeOffsetLocal(flay)).ToUniversalTime(),
                                                   linkType,
                                                   GetMNMFlFrameLength(flay),
                                                   GetMNMFlFrameLengthWire(flay)));
                }
                /* All higher versions uses MNMMediaType control and adds only supported types */
                else
                {
                    //foreach (var flay in sfs.MNMFrameTable.Where(flay => IsSupportedLinkType(ConvertMNMLayer2ToTCPDLayer2(GetMNMFlMediaType(sfs, flay)))))
                    if (!PmSupportedTypes.IsSupportedLinkType(ConvertMNMLayer2ToCommonLayer2(GetMNMFlMediaType(flay)))) continue; 
                    Frames.Add(new PmFrameMNM(BinReader,
                                                   flay,
                                                   ConvertMNMFrameTimeOffsetToRealTime(_mnmHeaderTimeStamp.AddSeconds(_timeZoneOffset), GetMNMFlTimeOffsetLocal(flay)).ToUniversalTime(),
                                                   linkType,
                                                   GetMNMFlFrameLength(flay),
                                                   GetMNMFlFrameLengthWire(flay)));
                }
            }
            //PrintError(sfs.MNMNumberOfFrames + "-" + sfs.Frames.Count);
        }

        #endregion

        #endregion

    }
}
